package org.etlt.job.config.xml;

import org.etlt.SettingCheck;
import org.etlt.job.JobContext;
import org.etlt.job.SettingResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * replaced by AbstractXmlResolver
 * @see AbstractXmlResolver
 * @param <T>
 */
@Deprecated
public abstract class AbstractSettingXmlResolver<T> implements SettingResolver<T> {

    protected AbstractSettingXmlResolver() {
        try {
            init();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    private static class SimpleErrorHandler implements ErrorHandler {

        @Override
        public void warning(SAXParseException e) {
            e.printStackTrace();
        }

        @Override
        public void fatalError(SAXParseException e) throws SAXException {
            throw e;
        }

        @Override
        public void error(SAXParseException e) throws SAXException {
            throw e;
        }

    }

    protected T resolve(Element element, JobContext context) {
        SegmentParser sp = getParserFactory().get(element.getNodeName());
        if (sp == null)
            throw new IllegalArgumentException("parser not found for " + element.getNodeName());
        T setting = (T) sp.parse(null, element, context);
        NodeList children = element.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node node = children.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                sp = getParserFactory().get(node.getNodeName());
                if (sp != null)
                    sp.parse(setting, node, context);
            }
        }
        if (setting instanceof SettingCheck) {
            ((SettingCheck) setting).check();
        }
        return setting;
    }

    @Override
    public T resolve(InputStream stream, JobContext context) {
        try {
            Document document = getDocumentBuilder().parse(stream);
            Element root = document.getDocumentElement();
            SegmentParser sp = getParserFactory().get(root.getNodeName());
            if (sp == null)
                throw new IllegalArgumentException("parser not found for " + root.getNodeName());
            T setting = (T) sp.parse(null, root, context);
            NodeList children = root.getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                Node node = children.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    sp = getParserFactory().get(node.getNodeName());
                    if (sp != null)
                        sp.parse(setting, node, context);
                }
            }
            if (setting instanceof SettingCheck) {
                ((SettingCheck) setting).check();
            }
            return setting;
        } catch (SAXException | IOException e) {
            throw new IllegalStateException("job.xml parse error.", e);
        }
    }

    private DocumentBuilder documentBuilder;

    private final Map<String, SegmentParser<T>> parserFactory = new HashMap<>();

    private final static String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
    private final static String XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";

    private final static String SQL_NAMESPACE = "@NAMESPACE";

    public DocumentBuilder getDocumentBuilder() {
        return documentBuilder;
    }

    public Map<String, SegmentParser<T>> getParserFactory() {
        return parserFactory;
    }

    public void init() throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        factory.setNamespaceAware(true);
        factory.setAttribute(JAXP_SCHEMA_LANGUAGE, XML_SCHEMA);
        documentBuilder = factory.newDocumentBuilder();
        documentBuilder.setEntityResolver(SchemaResolver.getInstance());
        documentBuilder.setErrorHandler(new SimpleErrorHandler());
        registerParser();
    }


    protected abstract void registerParser();

    private Node getFirstChildByName(Element element, String name) {
        NodeList nodes = element.getElementsByTagName(name);
        if (nodes != null && nodes.getLength() > 0) {
            return nodes.item(0);
        }
        return null;
    }

    protected String getAttributeValue(Element element, String name) {
        return element.getAttribute(name);
    }

    protected String getFirstChildValueByName(Element element, String name) {
        return getFirstChildValueByName(element, name, null);
    }

    protected String getFirstChildAttributeValueByName(Element element, String childName, String attributeName) {
        NodeList nodes = element.getElementsByTagName(childName);
        if (nodes != null && nodes.getLength() > 0) {
            for(int i = 0; i < nodes.getLength(); i++){
                Node node = nodes.item(i);
                if(node.getNodeType() == Node.ELEMENT_NODE){
                    Element child = (Element) node;
                    return child.getAttribute(attributeName);
                }
            }
        }
        return null;
    }

    protected List<String> getChildValuesByName(Element element, String name) {
        List<String> values = new ArrayList<>();
        NodeList nodes = element.getElementsByTagName(name);
        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);
            values.add(node.getTextContent());
        }
        return values;
    }

    protected String getFirstChildValueByName(Element element, String name, String defaultValue) {
        NodeList nodes = element.getElementsByTagName(name);
        if (nodes != null && nodes.getLength() > 0) {
            Node node = nodes.item(0);
            Optional<String> optional = Optional.of(node.getTextContent());
            return optional.get();
        }
        return defaultValue;
    }

}
